import scala.collection.immutable.ListSet
import scala.collection.immutable.HashSet

object Test extends dotty.runtime.LegacyApp {

  def testCorrectness(): Unit = {
    // a key that has many hashCode collisions
    case class Collision(i: Int) { override def hashCode = i / 5 }

    def subsetTest[T](emptyA:Set[T], emptyB:Set[T], mkKey:Int => T, n:Int): Unit = {
      val outside = mkKey(n + 1)
      for(i <- 0 to n) {
        val a = emptyA ++ (0 until i).map(mkKey)
        // every set must be a subset of itself
        require(a.subsetOf(a), "A set must be the subset of itself")
        for(k <- 0 to i) {
          // k <= i, so b is definitely a subset
          val b = emptyB ++ (0 until k).map(mkKey)
          // c has less elements than a, but contains a value that is not in a
          // so it is not a subset, but that is not immediately obvious due to size
          val c = b + outside
          require(b.subsetOf(a), s"$b must be a subset of $a")
          require(!c.subsetOf(a), s"$c must not be a subset of $a")
        }
      }
    }

    // test the HashSet/HashSet case
    subsetTest(HashSet.empty[Int], HashSet.empty[Int], identity, 100)

    // test the HashSet/other set case
    subsetTest(HashSet.empty[Int], ListSet.empty[Int], identity, 100)

    // test the HashSet/HashSet case for Collision keys
    subsetTest(HashSet.empty[Collision], HashSet.empty[Collision], Collision, 100)

    // test the HashSet/other set case for Collision keys
    subsetTest(HashSet.empty[Collision], ListSet.empty[Collision], Collision, 100)
  }

  /**
   * A main performance benefit of the new subsetOf is that we do not have to call hashCode during subsetOf
   * since we already have the hash codes in the HashSet1 nodes.
   */
  def testNoHashCodeInvocationsDuringSubsetOf() = {
    var count = 0

    case class HashCodeCounter(i:Int) {
      override def hashCode = {
        count += 1
        i
      }
    }

    val a = HashSet.empty ++ (0 until 100).map(HashCodeCounter)
    val b = HashSet.empty ++ (0 until 50).map(HashCodeCounter)
    val count0 = count
    val result = b.subsetOf(a)
    require(count == count0, "key.hashCode must not be called during subsetOf of two HashSets")
    result
  }
  testCorrectness()
  testNoHashCodeInvocationsDuringSubsetOf()
}
